const fs = require('fs-extra')
const { extname, join } = require('path')
const { format: formatTime, isValid } = require('date-fns')
const { isUndefined } = require('lodash')

const extensions = new Set([
    '.conf',
    '.cs',
    '.vue',
    '.map',
    '.appcache',
    '.gitignore',
    '.svg',
    '.manifest',
    '.ics',
    '.ifb',
    '.coffee',
    '.litcoffee',
    '.css',
    '.csv',
    '.html',
    '.htm',
    '.shtml',
    '.jade',
    '.js',
    '.mjs',
    '.jsx',
    '.less',
    '.md',
    '.markdown',
    '.mml',
    '.mdx',
    '.n3',
    '.txt',
    '.text',
    '.conf',
    '.def',
    '.list',
    '.log',
    '.in',
    '.ini',
    '.rtx',
    '.*rtf',
    '.sgml',
    '.sgm',
    '.shex',
    '.slim',
    '.slm',
    '.spdx',
    '.stylus',
    '.styl',
    '.tsv',
    '.t',
    '.tr',
    '.roff',
    '.man',
    '.me',
    '.ms',
    '.ttl',
    '.uri',
    '.uris',
    '.urls',
    '.vcard',
    '.vtt',
    '.wgsl',
    '.*xml',
    '.yaml',
    '.yml',
    '.dsc',
    '.curl',
    '.dcurl',
    '.mcurl',
    '.scurl',
    '.sub',
    '.ged',
    '.fly',
    '.flx',
    '.gv',
    '.3dml',
    '.spot',
    '.jad',
    '.wml',
    '.wmls',
    '.s',
    '.asm',
    '.c',
    '.cc',
    '.cxx',
    '.cpp',
    '.h',
    '.hh',
    '.dic',
    '.htc',
    '.f',
    '.for',
    '.f77',
    '.f90',
    '.hbs',
    '.java',
    '.lua',
    '.mkd',
    '.nfo',
    '.opml',
    '.*org',
    '.p',
    '.pas',
    '.pde',
    '.sass',
    '.scss',
    '.etx',
    '.sfv',
    '.ymp',
    '.uu',
    '.vcs',
    '.vcf',
    '.json',
    '.lock'
])

function forEach(object, name) {
    const kvs = []
    const traverse = (label, value, depth, paths = []) => {
        if (label) {
            paths = [...paths, label]
        }
        if (Array.isArray(value)) {
            if (label) {
                kvs.push({
                    label: label,
                    depth,
                    type: 'array',
                    id: paths.join('.')
                })
            }
            const deep = depth + 1
            value.forEach((item, idx) => {
                traverse(idx, item, deep, paths)
            })
        } else if (typeof value === 'object') {
            if (label) {
                kvs.push({
                    label: label,
                    depth,
                    type: 'object',
                    id: paths.join('.')
                })
            }
            for (let prop in value) {
                traverse(prop, value[prop], depth + 1, paths)
            }
        } else if (typeof value === 'string') {
            try {
                const json = JSON.parse(value)
                traverse(label, json, depth, paths)
            } catch (error) {
                if (/Time/.test(label) && isValid(value)) {
                    value = toTime(value)
                }
                kvs.push({
                    label: label,
                    value: value,
                    depth,
                    type: 'string',
                    id: paths.join('.')
                })
            }
        } else {
            kvs.push({
                label: label,
                value: value,
                depth,
                type: typeof value,
                id: paths.join('.')
            })
        }
    }
    traverse(name, object, 0)
    return kvs
}

async function isTextFile(path) {
    const ext = extname(path)
    return extensions.has(ext)
}

async function readPath(path, targets) {
    const paths = await fs.readdir(path)
    const items = []
    const utf_8 = { encoding: 'utf-8' }
    for (const it of paths) {
        const root = join(path, it)
        const item = {}
        for (const target of targets) {
            const subpath = join(root, target.name)
            switch (target.type) {
                case 'JSON':
                    item[target.id] = await fs.readJson(subpath, utf_8)
                    break
                case 'TEXT':
                    item[target.id] = await fs.readFile(subpath, utf_8)
                    break
            }
        }
        items.push(item)
    }
    return items
}

function toTime(time, format = 'yyyy-MM-dd HH:mm:ss') {
    return formatTime(time, format)
}

function now(format = 'yyyy-MM-dd HH:mm:ss') {
    const time = new Date()
    return formatTime(time, format)
}

function toUnitValue(value, time, digit) {
    let unitValue = (value / time).toFixed(digit)
    return parseFloat(unitValue)
}

function toRatio(value, total, digit) {
    let ratio = ((value / total) * 100).toFixed(digit)
    return parseFloat(ratio)
}
function toDuration(duration, formatString = 'Y年M月D天H时m分s秒S毫秒') {
    let units = [
        { unit: 'Y', value: 365 * 24 * 60 * 60 * 1000 },
        { unit: 'M', value: 30 * 24 * 60 * 60 * 1000 },
        { unit: 'D', value: 24 * 60 * 60 * 1000 },
        { unit: 'H', value: 60 * 60 * 1000 },
        { unit: 'm', value: 60 * 1000 },
        { unit: 's', value: 1000 },
        { unit: 'S', value: 1 }
    ]
    let value = {}
    for (const unit of units) {
        let int = Math.trunc(duration / unit.value)
        if (int > 1) {
            value[unit.unit] = int
            duration = duration % unit.value
            if (duration == 0) {
                break
            }
        }
    }
    return formatString.replace(/([YMDHmsS]+)([^YMDHmsS]*)/g, (_, prefix, suffix) => {
        let val = value[prefix]
        return isUndefined(val) ? '' : val + suffix
    })
}

module.exports = {
    isTextFile,
    forEach,
    readPath,
    toTime,
    now,
    toUnitValue,
    toRatio,
    toDuration
}
